class Compile {
    constructor(el, vm) {
        this.$el = document.querySelector(el);
        this.$vm = vm;
        // 剪切操作
        this.$fragment = this.node2Fragment(this.$el);
        // 执行编译
        this.Compile(this.$fragment)
        // 将编译完成的数据在放回去
        this.$el.appendChild(this.$fragment)
    }
    node2Fragment(el) {
        const fragment = document.createDocumentFragment();// 创建一个虚拟节点
        let child;
        // 搬空
        while(child = el.firstChild) {
            fragment.appendChild(child)
        }
        return fragment;
    }
    // 执行编译
    Compile(el) {
        const childNodes = el.childNodes; // 类数组
        Array.from(childNodes).forEach(item => {
            // 判断是文本节点还是元素节点
            if(item.nodeType === 1) {
                // 元素节点
                this.CompileElement(item)
            } else if(this.inter(item)){
                // 说明匹配到了文本节点 比如 {{name}}
                this.CompileText(item)
            }
        })
    }
    inter(node) {
        // 判断是不是文本节点 且带有{{}}
        return node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.textContent)
    }
    CompileText(node) {
        const exp = RegExp.$1;
        // 初始化赋值
        this.textUpdator(node, this.$vm[exp])
        // 创建一个订阅者  当元素发生改变时通知订阅者进行更新
        new Watcher(this.$vm, exp, () => {
            this.textUpdator(node, this.$vm[exp])
        })
    }
    textUpdator(node,value) {
        node.textContent = value;
    }

    CompileElement(node) {
        const nodeAttrs = node.attributes;
        Array.from(nodeAttrs).forEach(attr => {
            // 规定：指令以k-xxx="yyy"命名
            const attrName = attr.name; // 属性名称 k-xxx
            const exp = attr.value;// 属性值 yyy
            if (attrName.indexOf('k-') == 0) {
                const dir = attrName.substring(2);; // k-html html
                // 执行指令解析
                this[dir] && this[dir](node, exp)
            }
        })
    }
    html(node, exp) {
        node.innerHTML = this.$vm[exp];
        // 创建一个订阅者  当元素发生改变时通知订阅者进行更新
        new Watcher(this.$vm, exp, () => {
            node.innerHTML = this.$vm[exp]
        })
    }
    text(node, exp) {
        node.textContent = this.$vm[exp];
        // 创建一个订阅者  当元素发生改变时通知订阅者进行更新
        new Watcher(this.$vm, exp, () => {
            node.textContent = this.$vm[exp]
        })
    }
}